package com.xd.common.http;

import cn.hutool.core.map.MapUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.xd.common.constant.SysConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;


/**
 * http 基类
 */
@Slf4j
public class HttpUtil {
    //请求GET
    public static final String GET = "GET";
    //请求POST
    public static final String POST = "POST";
    //编码
    public static final String ENCODING = "UTF-8";
    //默认撞库超时时间（毫秒）
    public static final int TIMEOUT = 2500;

    //入库超时时间（毫秒）
    public static final int ENTER_TIMEOUT = 7500;

    /**
     * GET 请求无参数
     *
     * @param url 地址
     * @return ResponeResult
     */
    public static ResponseResult get(String url, Integer timeout) {
        return getH(url, null, timeout);
    }

    public static ResponseResult getH(String url, Map<String, Object> header, Integer timeout) {
        return http(url, GET, null, null, null, header, timeout);
    }

    /**
     * POST 请求无参数
     *
     * @param url 地址
     * @return ResponeResult
     */
    public static ResponseResult post(String url, Integer timeout) {
        return postH(url, null, null, timeout);
    }

    /**
     * POST 请求无参数
     *
     * @param url 地址
     * @return ResponeResult
     */
    public static ResponseResult postH(String url, Map<String, Object> header, Integer timeout) {
        return postH(url, null, header, timeout);
    }


    /**
     * GET 请求
     *
     * @param url   地址
     * @param param 参数(实体类或Map)
     * @return ResponeResult
     */
    public static ResponseResult get(String url, Object param, Integer timeout) {
        return getH(url, param, null, timeout);
    }

    public static ResponseResult getH(String url, Object param, Map<String, Object> header, Integer timeout) {
        return http(url, GET, null, param, null, header, timeout);
    }

    /**
     * POST 请求 json
     *
     * @param url   地址
     * @param param 参数(实体类或json串)
     * @return ResponeResult
     */
    public static ResponseResult post(String url, Object param, Integer timeout) {
        return postH(url, param, null, timeout);
    }

    public static ResponseResult postH(String url, Object param, Map<String, Object> header, Integer timeout) {
        return http(url, POST, "application/json", null, param, header, timeout);
    }

    /***
     * http 请求
     * @param url 地址
     * @param method 请求方式GET | POST
     * @param contentType 请求类型
     * @param params 参数
     * @param json json参数
     * @param header 请求头
     * @return ResponeResult
     */
    public static ResponseResult http(String url, String method, String contentType, Object params, Object json, Map<String, Object> header, Integer timeout) {
        ResponseResult responseResult = new ResponseResult();
        DataOutputStream out = null;
        InputStream is = null;
        Integer time = null;
        String msg = "";
        try {
            if (params != null) {
                int i = 0;
                StringBuffer param = new StringBuffer();
                if (params instanceof Map) {
                    Map ps = (Map) params;
                    for (Object key : ps.keySet()) {
                        Object value = ps.get(key);
                        if (ObjectUtils.isNotEmpty(value)) {
                            if (i == 0)
                                param.append("?");
                            else
                                param.append("&");
                            param.append(key).append("=").append(value);
                            i++;
                        }
                    }
                } else {
                    Field[] fields = params.getClass().getDeclaredFields();
                    for (Field field : fields) {
                        field.setAccessible(true);
                        String fieldName = field.getName();
                        try {
                            Object value = field.get(params);
                            if (ObjectUtils.isNotEmpty(value)) {
                                if (i == 0)
                                    param.append("?");
                                else
                                    param.append("&");
                                param.append(fieldName).append("=").append(value);
                                i++;
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
                url += param;
            }
//            if (log.isInfoEnabled())
//                log.info("\n[ ======== Http请求 ======== ] url:" + url + " ,jsonParams:" + json + (null == header ? "" : " ,header:" + header));
            URL httpURL = new URL(url);
            // 打开和URL之间的连接
            HttpURLConnection connection = (HttpURLConnection) httpURL.openConnection();
            connection.setRequestMethod(method);
            // 设置通用的请求属性
            if (StringUtils.isNotBlank(contentType))
                connection.setRequestProperty("Content-Type", contentType);

            //请求头
            if (MapUtil.isNotEmpty(header))
                for (Map.Entry<String, Object> entry : header.entrySet())
                    connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
            //例子
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
//            time = null == timeout ? 10000 : timeout;
//            time = timeout < 9000 ? 9000 : timeout;
            time = null == timeout ? TIMEOUT : timeout;
            connection.setConnectTimeout(time);
            connection.setReadTimeout(time);
            if (ObjectUtils.isNotEmpty(json)) {
                String js;
                if (json instanceof String)
                    js = (String) json;
                else
                    js = JSON.toJSONString(json);
                responseResult.setParam(js);
                // 得到请求的输出流对象
                out = new DataOutputStream(connection.getOutputStream());
                out.write(js.getBytes(HttpUtil.ENCODING));
                out.flush();
            }
            msg = "\n[ ======== Http请求 ======== ] url:" + url + " ,jsonParams:" + json + (null == header ? "" : " ,header:" + header);
            // 建立实际的连接
            connection.connect();
            is = connection.getInputStream();
            String jsonData = IOUtils.toString(is, Charset.forName(HttpUtil.ENCODING));
            if (time >= ENTER_TIMEOUT)
                log.info(msg + "  Http响应结果：" + jsonData);
            return responseResult.setSuccess(true).setJsonData(jsonData);
        } catch (IOException e) {
            if (time >= ENTER_TIMEOUT)
                log.info(msg);
            if (e instanceof SocketTimeoutException) {
                responseResult.setStatus(SysConstant.XKD_TIME_OUT);
            } else {
                responseResult.setStatus(SysConstant.XKD_ERROR);
                log.info("[Http请求异常]:" + e.getMessage());
            }
            responseResult.setSuccess(false);
            responseResult.setMessage(e.toString());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    log.error("[流关闭失败]", e);
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    log.error("[流关闭失败]", e);
                }
            }
        }
        return responseResult;
    }

    private static String charset = "utf-8";

    /**
     * 表单提交Post
     *
     * @param url
     * @param map
     * @return
     */
    public static ResponseResult doPost(String url, Map<String, Object> map, Integer timeout) {
        return doPostH(url, map, null, timeout);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static ResponseResult doPostH(String url, Map<String, Object> map, Map<String, Object> header, Integer timeout) {
        ResponseResult responseResult = new ResponseResult();
        HttpPost httpPost = null;
        String result = null;
        CloseableHttpResponse response = null;
        CloseableHttpClient httpClient = null;
        Integer time = null;
        String msg = "";
        try {
            httpPost = new HttpPost(url);
            // 设置参数
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            Iterator iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Object> elem = (Map.Entry<String, Object>) iterator.next();
                list.add(new BasicNameValuePair(elem.getKey(), elem.getValue().toString()));
            }
            if (!list.isEmpty()) {
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, charset);
                httpPost.setEntity(entity);
            }
            //请求头
            if (MapUtil.isNotEmpty(header))
                for (Map.Entry<String, Object> entry : header.entrySet())
                    httpPost.setHeader(entry.getKey(), entry.getValue().toString());
//            time = timeout<9000 ? 9000 : timeout;
//            time = null == timeout ? 10000 : timeout;
            time = null == timeout ? TIMEOUT : timeout;
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(time) // 设置连接超时时间
                    .setSocketTimeout(time)
                    .build();
            httpClient = HttpClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .build();
            responseResult.setParam(JSON.toJSONString(map));
            msg = "\n[ ======== Http请求 ======== ] url:" + url + " ,jsonParams:" + map + (null == header ? "" : " ,header:" + header);
            response = httpClient.execute(httpPost);
            if (response != null) {
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    result = EntityUtils.toString(resEntity, charset);
                    if (time >= ENTER_TIMEOUT)
                        log.info(msg + " Http响应结果：" + result);
                    return responseResult.setSuccess(true).setJsonData(result);
                }
            }
        } catch (Exception e) {
            if (time >= ENTER_TIMEOUT)
                log.info(msg);
            if (e instanceof SocketTimeoutException) {
                responseResult.setStatus(SysConstant.XKD_TIME_OUT);
            } else {
                responseResult.setStatus(SysConstant.XKD_ERROR);
                log.info("[Http请求异常]", e);
            }
            responseResult.setSuccess(false);
            responseResult.setMessage(e.toString());
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    log.error("[流关闭失败]", e);
                }
            }
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    log.error("[流关闭失败]", e);
                }
            }
        }
        return responseResult;
    }

    /**
     * 将通知参数转化为字符串
     *
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取浏览器名字
     *
     * @param request 请求
     * @return {@link String}
     */
    public static String getBrowserName(HttpServletRequest request) {
        String uaStr = request.getHeader("User-Agent");
        UserAgent ua = UserAgentUtil.parse(uaStr);
        return ua.getBrowser().getName();
    }

    /**
     * 获取浏览器版本
     *
     * @param request 请求
     * @return {@link String}
     */
    public static String getBrowserVersion(HttpServletRequest request) {
        String uaStr = request.getHeader("User-Agent");
        UserAgent ua = UserAgentUtil.parse(uaStr);
        return ua.getBrowser().getVersion(uaStr);
    }

    /**
     * 获取操作系统名称
     *
     * @param request 请求
     * @return {@link String}
     */
    public static String getOsName(HttpServletRequest request) {
        String uaStr = request.getHeader("User-Agent");
        UserAgent ua = UserAgentUtil.parse(uaStr);
        return ua.getOs().getName();
    }


}
